import scipy
from helper_codes.general_stimulus import general_stimulus, GeneralStim

def sine_grating_settings():
    '''
    This function tells you what settings are needed and in what order for this stimulus.
    Inputs:
        None
    Returns:
        settings        : A list of tupples.  In each tupple is
                            (setting_name, setting_description, default_value).
                          The list is of length num_settings + 1, so that the final
                             value in the list is True/False wether or not the stimulus
                             can be triggered via the parallel port or not.
    '''
    settings = []
    settings.append( ('spacial_frequency',
                     'inner (first) and outer (second) spacial frequency of the grating. (cycles per unit) [list]',
                     [1,2] ))
    settings.append( ('temporal_frequency',
                     'inner (first) and outer (second) temporal frequency of the grating. (Hz) [list]',
                     [1,2] ))
    settings.append( ('orientation',
                     'inner (first) and outer (second) orientation (degrees) [list]',
                     [90,180] ))
    settings.append( ('contrast',
                     'inner (first) and outer (second) grating contrast (0.0-1.0) [list]',
                     [1.0,1.0] ))
    settings.append( ('radius',
                     'inner (first) and outer (second) grating radius (normalized units) [list]',
                     [1.0,3.0] ))
    settings.append( ('duration',
                     'How long the stimulus will last. (seconds)',
                     20.0 ))
    settings.append( ('win_size',
                     'The dimensions of the stimulus delivery window. (pixels) [list]',
                     [1440,900] ))
    settings.append( ('refresh_rate',
                     'The number of screen refreshes per second. (Hz)',
                     60 ))
    settings.append(True) # this stimulus can be triggered
    return settings

def sine_grating_runtime(spacial_frequency, temporal_frequency, orientation, contrast, 
                         radius, duration, win_size, refresh_rate): 
    """
    Calculate and return the number of seconds that this stimulus will need to run, after being triggered.
    """
    return duration + 0.99 # 0.99 added so rounding is always up.

class Stim(GeneralStim):
    """
    This is a basic stimulus object, it must have:
        .setup(window) method to set itself up to be drawn
        .draw() method to draw itself to the display
        .evolve() method to set itself up for the next draw
        .exhausted variable set to True after stim is done.
    """
    def __init__( self, spacial_frequency, temporal_frequency, orientation, 
                  contrast, radius, duration, refresh_rate, 
                  preview, save_frames):
        self.spacial_frequency = spacial_frequency
        self.temporal_frequency = temporal_frequency
        self.orientation = orientation
        self.contrast = contrast
        self.radius = radius
        self.duration = duration

        self.refresh_rate = refresh_rate
        self.preview = preview
        self.save_frames = save_frames

        self.exhausted = False
        self.times_evolved = 0

    def setup(self, window):
        from psychopy import visual

        rad1 = self.radius[0]
        sf1  = self.spacial_frequency[0]
        ori1 = self.orientation[0]
        con1 = self.contrast[0]
        rad2 = self.radius[1]
        sf2  = self.spacial_frequency[1]
        ori2 = self.orientation[1]
        con2 = self.contrast[1]
        mask = 'stimuli/helper_codes/circle_512.gif'

        self.num_frames = int( self.duration * self.refresh_rate)

        # set up the grating patches
        self.inner_grating = visual.PatchStim( window, tex='sin', 
                                          size=[rad1, rad1], 
                                          units='norm', ori=ori1, 
                                          sf=[sf1, sf1],
                                          rgb = con1, 
                                          mask=mask)
        self.outer_grating = visual.PatchStim( window, tex='sin', 
                                          size=[rad2,rad2], 
                                          units='norm', ori=ori2, 
                                          sf=[sf2, sf2],
                                          rgb = con2, 
                                          mask=mask)
        self.textures = [self.inner_grating, self.outer_grating]


    def draw(self):
        self.outer_grating.draw()
        self.inner_grating.draw()

    def evolve(self):
        self.times_evolved += 1

        self.inner_grating.setPhase( self.temporal_frequency[0] * self.times_evolved *
                                     (1.0/self.refresh_rate) )
        self.outer_grating.setPhase( self.temporal_frequency[1] * self.times_evolved *
                                     (1.0/self.refresh_rate) )

        if self.times_evolved >= self.num_frames:
            if (not self.preview) or self.save_frames:
                self.exhausted = True
            else:
                self.times_evolved = 0 # continue looping forever in preview mode.

def sine_grating( spacial_frequency, temporal_frequency, orientation, contrast, 
                  radius, duration, win_size, refresh_rate, 
                     window=None,
                     eye_cup=None, 
                     rf=None, 
                     save_frames=False,
                     save_frames_factor=1.0,
                     save_frames_size=100,
                     display_saved_frames=False,
                     preview=True):
    """
    This stimulus is a moving sine grating with an inner and outer region.
    Inputs:
        spacial_frequency   : inner (first) and outer (second) spacial frequency 
                              of the grating. (cycles per unit) [list]
        temporal_frequency  : inner (first) and outer (second) temporal frequency 
                              of the grating. (Hz) [list]
        orientation         : inner (first) and outer (second) orientation (degrees) [list]
        contrast            : inner (first) and outer (second) grating contrast (0.0-1.0) [list]
        radius              : inner (first) and outer (second) grating radius (normalized units) [list]
        duration        : how long the stimulus will last. (seconds)
        win_size        : dimensions of the screen in pixels, (1440,900) is default
        refresh_rate    : refresh rate in Hz of the video, 60.0 is default.
        random_seed     : the seed for the random number generator.
        --- kwargs --
        window          : a psychopy.visual Window object, if not passed it will be constructed.
                        :    if passed it will not be closed (a blank screen will be drawn)
        eye_cup         : An Eye_cup object so we can draw the eye_cup on the screen and
                             set up the normalized coordinate system (size of eye_cup)
        rf              : an RF object so we can set up the normalized coordinate system
        frame_info      : if None, then no frames are saved, otherwise it is a 
                                dictionary with 
                                    'factor':float 
                                so we can scale down the frames and
                                output a frames.gif (an animated gif file)
                                and 
                                    'times':list
                                so we know how many frames to render.
        preview         : if True then the frames will display immediately,
                             otherwise we will wait for a pulse on the parallel port.
    Returns:
        times           : The times when frames were shown, with zero being the first frame time.
    """
    shade = 1.0
    stim_list = [ Stim( spacial_frequency, temporal_frequency, orientation, 
                        contrast, radius, duration, refresh_rate, 
                        preview, save_frames ) ]
    return_values = general_stimulus( stim_list, shade, win_size, 
                                         window=window,
                                         eye_cup=eye_cup, 
                                         rf=rf, 
                                         save_frames=save_frames,
                                         save_frames_factor=save_frames_factor,
                                         save_frames_size=save_frames_size,
                                         display_saved_frames=display_saved_frames,
                                         preview=preview)
    return return_values




if __name__ == '__main__':
    # ---------------------------------
    # -- Set the stimulus parameters --
    # ---------------------------------
    
    spacial_frequency   = [1, 2]
    temporal_frequency  = [1, 1.5]
    orientation         = [90, 90]
    contrast            = [1.0, 1.0]
    radius              = [1.0, 3.0]
    duration            = 15.0

    # display parameters
    win_size = (1440,900)  # in pixels
    refresh_rate = 60.0    # in Hz
    random_seed = 40

    from helper_codes.RF import RF
    rf = RF.from_rf_file()

    return_values = sine_grating( spacial_frequency, temporal_frequency, 
                                  orientation, contrast, radius, duration, 
                                  win_size, refresh_rate, 
                                  preview=True )

